using System;
using System.Collections.Generic;
using System.Text;
using System.Windows.Forms;
using System.Drawing;
using System.ComponentModel;

using SBPweb.Controls.Windows;
using SBPweb.Themes.Windows;
using SBPweb.Themes.Windows.Skins;

namespace SBPweb.Controls.Windows.Common
{
	/// <summary>
	/// Class of Button
	/// </summary>
	[ToolboxBitmap(typeof(System.Windows.Forms.Button))]
	public partial class Button : System.Windows.Forms.Button, ISkinnable
	{
		#region Private members

		private bool mUseDefaultSkin;
		private Skin mSkin;
		private Margin mSizingMargin;
		private Margin mContentMargin;
		private MouseEventState mMState = MouseEventState.None;
		private SizeF size;
		private string mToolTipString;
		private ToolTip mToolTip;
		private bool mPaintToClip = false;

		#endregion

		#region Constructor logic

		public Button()
		{
			SetStyle();
			InitializeComponent();
			InitializeMargins();
			
			RegisterEventHandlers();
		}

		public Button(Skin skin)
		{
			mSkin = skin;
			SetStyle();
			InitializeComponent();
			InitializeMargins();

			RegisterEventHandlers();
		}

		private void SetStyle()
		{
			base.SetStyle(ControlStyles.ResizeRedraw | ControlStyles.OptimizedDoubleBuffer | ControlStyles.UserPaint | ControlStyles.AllPaintingInWmPaint | ControlStyles.SupportsTransparentBackColor, true);
		}

		private void RegisterEventHandlers()
		{
			this.MouseEnter += new EventHandler(this_MouseEnter);
			this.MouseLeave += new EventHandler(this_MouseLeave);
			this.MouseDown += new MouseEventHandler(this_MouseDown);
			this.MouseUp += new MouseEventHandler(this_MouseUp);
			this.MouseHover += new EventHandler(this_MouseHover);
			DefaultLookAndFeel.OnSkinChanged += new EventHandler(DefaultLookAndFeel_OnSkinChanged);
		}

		private void InitializeMargins()
		{
			if (Skin == null)
			{
				mSizingMargin = new Margin();
				mContentMargin = new Margin();
			}
			else
			{
				mSizingMargin = new Margin(Skin.SelectSingleNode("Elements/Button/Image/SizingMargins"));
				mContentMargin = new Margin(Skin.SelectSingleNode("Elements/Button/ContentMargins"));
			}
		}

		#endregion

		#region Properties

		public string ToolTip
		{
			get
			{
				return mToolTipString;
			}
			set
			{
				mToolTipString = value;
				this.mToolTip.SetToolTip(this, value);
			}
		}

		public bool PaintToClip
		{
			get
			{
				return mPaintToClip;
			}
			set
			{
				mPaintToClip = value;
			}
		}

		#endregion

		#region Custom draw

		protected override void OnPaint(PaintEventArgs e)
		{
			Rectangle clipRect;

			if (mPaintToClip)
			{
				clipRect = e.ClipRectangle;
			}
			else
			{
				clipRect = this.ClientRectangle;
			}

			if (Skin == null)
			{
				base.OnPaint(e);

				StringFormat sf = new StringFormat(StringFormat.GenericDefault);
				size = e.Graphics.MeasureString(this.Text, this.Font, new SizeF(clipRect.Width, clipRect.Height), sf);
				this.MinimumSize = new System.Drawing.Size(Convert.ToInt32(mSizingMargin.Horizontal + mContentMargin.Vertical + size.Width + 5), Convert.ToInt32(mSizingMargin.Vertical + mContentMargin.Vertical + size.Height + 5));
				
				return;
			}

			base.OnPaintBackground(e);

			mSizingMargin = Skin.CachedProperties.ButtonSizingMargin;
			mContentMargin = Skin.CachedProperties.ButtonContentMargin;

			System.Drawing.Image img = Skin.CachedProperties.ButtonImage;
			bool verticalLayout = false;
			if (Skin.CachedProperties.ButtonLayout == LayoutDirection.Vertical) verticalLayout = true;

			int imgCount = 5;
			int imgWidth = img.Width;
			int imgHeight = (Int32)(img.Height / imgCount);
			int imgTop;
			int imgLeft;

			if (!verticalLayout)
			{
				imgWidth = (Int32)(img.Width / imgCount);
				imgHeight = img.Height;
				imgTop = 0;

				switch (mMState)
				{
					default:
					case MouseEventState.None:
						if (this.Enabled)
						{
							imgLeft = 0;
						}
						else
						{
							imgLeft = 3 * imgWidth;
						}
						break;
					case MouseEventState.OnHover:
						imgLeft = 1 * imgWidth;
						break;
					case MouseEventState.OnDown:
						if (this.Enabled)
						{
							imgLeft = 2 * imgWidth;
						}
						else
						{
							imgLeft = 4 * imgWidth;
						}
						break;
				}
			}
			else
			{
				imgLeft = 0;
				switch (mMState)
				{
					default:
					case MouseEventState.None:
						if (this.Enabled)
						{
							imgTop = 0;
						}
						else
						{
							imgTop = 3 * imgHeight;
						}
						break;
					case MouseEventState.OnHover:
						imgTop = 1 * imgHeight;
						break;
					case MouseEventState.OnDown:
						if (this.Enabled)
						{
							imgTop = 2 * imgHeight;
						}
						else
						{
							imgTop = 4 * imgHeight;
						}
						break;
				}
			}

			#region Image parts
			Rectangle iLT = new Rectangle(imgLeft, imgTop, mSizingMargin.Left, mSizingMargin.Top);
			Rectangle iT = new Rectangle(imgLeft + mSizingMargin.Left, imgTop, imgWidth - mSizingMargin.Horizontal, mSizingMargin.Top);
			Rectangle iRT = new Rectangle(imgLeft + imgWidth - mSizingMargin.Right, imgTop, mSizingMargin.Right, mSizingMargin.Top);
			Rectangle iLM = new Rectangle(imgLeft, imgTop + mSizingMargin.Top, mSizingMargin.Left, (imgHeight - 1) - mSizingMargin.Vertical);
			Rectangle iM = new Rectangle(imgLeft + mSizingMargin.Left, imgTop + mSizingMargin.Top, imgWidth - mSizingMargin.Horizontal, (imgHeight - 1) - mSizingMargin.Vertical);
			Rectangle iRM = new Rectangle(imgLeft + imgWidth - mSizingMargin.Right, imgTop + mSizingMargin.Top, mSizingMargin.Right, (imgHeight - 1) - mSizingMargin.Vertical);
			Rectangle iLB = new Rectangle(imgLeft, imgTop + imgHeight - mSizingMargin.Bottom, mSizingMargin.Left, mSizingMargin.Bottom);
			Rectangle iB = new Rectangle(imgLeft + mSizingMargin.Left, imgTop + imgHeight - mSizingMargin.Bottom, imgWidth - mSizingMargin.Horizontal, mSizingMargin.Bottom);
			Rectangle iRB = new Rectangle(imgLeft + imgWidth - mSizingMargin.Right, imgTop + imgHeight - mSizingMargin.Bottom, mSizingMargin.Right, mSizingMargin.Bottom);
			#endregion

			#region Window parts
			Rectangle tLT = new Rectangle(clipRect.Left, clipRect.Top, mSizingMargin.Left, mSizingMargin.Top);
			Rectangle tT = new Rectangle(clipRect.Left + mSizingMargin.Left, clipRect.Top, clipRect.Width - mSizingMargin.Horizontal, mSizingMargin.Top);
			Rectangle tRT = new Rectangle(clipRect.Right - mSizingMargin.Right, clipRect.Top, mSizingMargin.Right, mSizingMargin.Top);
			Rectangle tLM = new Rectangle(clipRect.Left, clipRect.Top + mSizingMargin.Top, mSizingMargin.Left, clipRect.Height - mSizingMargin.Vertical);
			Rectangle tM = new Rectangle(clipRect.Left + mSizingMargin.Left, clipRect.Top + mSizingMargin.Top, clipRect.Width - mSizingMargin.Horizontal, clipRect.Height - mSizingMargin.Vertical);
			Rectangle tRM = new Rectangle(clipRect.Right - mSizingMargin.Right, clipRect.Top + mSizingMargin.Top, mSizingMargin.Right, clipRect.Height - mSizingMargin.Vertical);
			Rectangle tLB = new Rectangle(clipRect.Left, clipRect.Bottom - mSizingMargin.Bottom, mSizingMargin.Left, mSizingMargin.Bottom);
			Rectangle tB = new Rectangle(clipRect.Left + mSizingMargin.Left, clipRect.Bottom - mSizingMargin.Bottom, clipRect.Width - mSizingMargin.Horizontal, mSizingMargin.Bottom);
			Rectangle tRB = new Rectangle(clipRect.Right - mSizingMargin.Right, clipRect.Bottom - mSizingMargin.Bottom, mSizingMargin.Right, mSizingMargin.Bottom);
			#endregion

			#region Drawing
			e.Graphics.DrawImage(img, tLT, iLT, GraphicsUnit.Pixel);
			e.Graphics.DrawImage(img, tT, iT, GraphicsUnit.Pixel);
			e.Graphics.DrawImage(img, tRT, iRT, GraphicsUnit.Pixel);
			e.Graphics.DrawImage(img, tLM, iLM, GraphicsUnit.Pixel);
			e.Graphics.DrawImage(img, tM, iM, GraphicsUnit.Pixel);
			e.Graphics.DrawImage(img, tRM, iRM, GraphicsUnit.Pixel);
			e.Graphics.DrawImage(img, tLB, iLB, GraphicsUnit.Pixel);
			e.Graphics.DrawImage(img, tB, iB, GraphicsUnit.Pixel);
			e.Graphics.DrawImage(img, tRB, iRB, GraphicsUnit.Pixel);

			if (this.Image != null)
			{
				DrawImage(e, tM);
			}
			DrawText(e);
			#endregion
		}

		protected override void OnPaintBackground(PaintEventArgs e)
		{
			base.OnPaintBackground(e);
		}

		public void PaintTo(Graphics g, Rectangle r)
		{
			OnPaint(new PaintEventArgs(g, r));
		}

		public void PaintBackgroundTo(Graphics g, Rectangle r)
		{
			OnPaintBackground(new PaintEventArgs(g, r));
		}

		private void DrawImage(PaintEventArgs e, Rectangle drawableArea)
		{
			int DistanceBetweenTextAndImage = 2;
			Point drawAreaStartLoc = new Point((this.Width - drawableArea.Width) / 2, (this.Height - drawableArea.Height) / 2);

			int imageStartX = drawableArea.Width / 2 - this.Image.Width / 2;
			int iamgeStartY = drawableArea.Height / 2 - this.Image.Height / 2;
			Rectangle imageSource = new Rectangle();
			Rectangle imageDestination = new Rectangle();
			if (imageStartX < 0)
			{
				imageSource.X = imageStartX * (-1);
				imageSource.Width = drawableArea.Width;
				imageDestination.X = 0 + drawAreaStartLoc.X;
				imageDestination.Width = drawableArea.Width;
			}
			else
			{
				imageSource.X = 0;
				imageSource.Width = this.Image.Width;
				imageDestination.X = drawableArea.Width / 2 - this.Image.Width / 2 + drawAreaStartLoc.X;
				imageDestination.Width = this.Image.Width;
			}
			if (iamgeStartY < 0)
			{
				imageSource.Y = iamgeStartY * (-1);
				imageSource.Height = drawableArea.Height;
				imageDestination.Y = 0 + drawAreaStartLoc.Y;
				imageDestination.Height = drawableArea.Height;
			}
			else
			{
				imageSource.Y = 0;
				imageSource.Height = this.Image.Height;
				imageDestination.Y = drawableArea.Height / 2 - this.Image.Height / 2 + drawAreaStartLoc.Y;
				imageDestination.Height = this.Image.Height;
			}
			if (this.ImageAlign == ContentAlignment.BottomLeft || this.ImageAlign == ContentAlignment.MiddleLeft || this.ImageAlign == ContentAlignment.TopLeft)
			{
				imageDestination.X = drawAreaStartLoc.X;
			}
			if (this.ImageAlign == ContentAlignment.BottomRight || this.ImageAlign == ContentAlignment.MiddleRight || this.ImageAlign == ContentAlignment.TopRight)
			{
				imageDestination.X = drawAreaStartLoc.X + drawableArea.Width - this.Image.Width;
			}
			if (this.ImageAlign == ContentAlignment.TopCenter || this.ImageAlign == ContentAlignment.TopLeft || this.ImageAlign == ContentAlignment.TopRight)
			{
				imageDestination.Y = drawAreaStartLoc.Y;
			}
			if (this.ImageAlign == ContentAlignment.BottomLeft || this.ImageAlign == ContentAlignment.BottomCenter || this.ImageAlign == ContentAlignment.BottomRight)
			{
				imageDestination.Y = drawAreaStartLoc.Y + drawableArea.Height - this.Image.Height;
			}

			if (this.TextImageRelation != TextImageRelation.Overlay)
			{
				RectangleF textRect = new RectangleF();
				string text = null;
				int pos = -1;
				SizeF sizeStart = new SizeF();
				SizeF sizeShCut = new SizeF();
				Color c = new Color();
				GenerateDrawText(e, out textRect, out text, out pos, out sizeStart, out sizeShCut, out c);

				//Change locations
				if (this.TextImageRelation == TextImageRelation.ImageAboveText)
				{
					int fullHeight = (int)size.Height + DistanceBetweenTextAndImage + imageSource.Height;
					if (fullHeight > drawableArea.Height)
					{
						textRect.Y = drawableArea.Height - size.Height + drawAreaStartLoc.Y;
						imageDestination.Y = (int)textRect.Y - DistanceBetweenTextAndImage - imageSource.Height;
					}
					else
					{
						textRect.Y = drawableArea.Height - (drawableArea.Height - fullHeight) / 2 - size.Height + drawAreaStartLoc.Y;
						imageDestination.Y = (int)textRect.Y - DistanceBetweenTextAndImage - imageSource.Height;
					}
					if (imageDestination.Y < drawAreaStartLoc.Y)
					{
						int distance = drawAreaStartLoc.Y - imageDestination.Y;
						imageDestination.Y += distance;
						imageDestination.Height -= distance;
						imageSource.Y += distance;
						imageSource.Height -= distance;
					}
				}
				else if (this.TextImageRelation == TextImageRelation.TextAboveImage)
				{
					int fullHeight = (int)size.Height + DistanceBetweenTextAndImage + this.Image.Height;
					if (fullHeight > drawableArea.Height)
					{
						textRect.Y = drawAreaStartLoc.Y;
						imageDestination.Y = (int)textRect.Y + (int)size.Height + DistanceBetweenTextAndImage;
					}
					else
					{
						textRect.Y = (drawableArea.Height - fullHeight) / 2;
						imageDestination.Y = (int)textRect.Y + (int)size.Height + DistanceBetweenTextAndImage;
					}
					if (imageDestination.Y + imageSource.Height > drawableArea.Y + drawableArea.Height)
					{
						int distance = (imageDestination.Y + imageSource.Height) - (drawableArea.Y + drawableArea.Height);
						imageDestination.Height -= distance;
						imageSource.Height -= distance;
					}
				}
				else if (this.TextImageRelation == TextImageRelation.ImageBeforeText)
				{
					int fullWidth = (int)size.Width + DistanceBetweenTextAndImage + imageSource.Width;
					if (fullWidth > drawableArea.Width)
					{
						textRect.X = drawableArea.Width - size.Width + drawAreaStartLoc.X;
						imageDestination.X = (int)textRect.X - DistanceBetweenTextAndImage - imageSource.Width;
					}
					else
					{
						textRect.X = drawableArea.Width - (drawableArea.Width - fullWidth) / 2 - size.Width + drawAreaStartLoc.X;
						imageDestination.X = (int)textRect.X - DistanceBetweenTextAndImage - imageSource.Width;
					}
					if (imageDestination.X < drawAreaStartLoc.X)
					{
						int distance = drawAreaStartLoc.X - imageDestination.X;
						imageDestination.X += distance;
						imageDestination.Width -= distance;
						imageSource.X += distance;
						imageSource.Width -= distance;
					}
				}
				else if (this.TextImageRelation == TextImageRelation.TextBeforeImage)
				{
					int fullWidth = (int)size.Width + DistanceBetweenTextAndImage + imageSource.Width;
					if (fullWidth > drawableArea.Width)
					{
						textRect.X = drawAreaStartLoc.X;
						imageDestination.X = (int)textRect.X + (int)size.Width + DistanceBetweenTextAndImage;
					}
					else
					{
						textRect.X = (drawableArea.Width - fullWidth) / 2;
						imageDestination.X = (int)textRect.X + (int)size.Width + DistanceBetweenTextAndImage;
					}
					if (imageDestination.X + imageSource.Width > drawableArea.X + drawableArea.Width)
					{
						int distance = (imageDestination.X + imageSource.Width) - (drawableArea.X + drawableArea.Width);
						imageDestination.Width -= distance;
						imageSource.Width -= distance;
					}
				}

				e.Graphics.DrawString(text, this.Font, new SolidBrush(c), textRect);
				if (pos != -1)
				{
					e.Graphics.DrawLine(new Pen(c), textRect.X + sizeStart.Width + 1, textRect.Y + size.Height, textRect.X + sizeStart.Width + sizeShCut.Width - 2, textRect.Y + size.Height);
				}
			}

			e.Graphics.DrawImage(Image, imageDestination, imageSource, GraphicsUnit.Pixel);
		}

		private void GenerateDrawText(PaintEventArgs e, out RectangleF drawRect, out string text, out int pos, out SizeF sizeStart, out SizeF sizeShCut, out Color c)
		{
			StringFormat sf = new StringFormat(StringFormat.GenericDefault);
			text = this.Text;
			string textStart = string.Empty;
			string textShCut = string.Empty;
			pos = text.IndexOf("&");
			if (pos != -1)
			{
				text = text.Remove(pos, 1);
				textStart = text.Substring(0, pos);
				textShCut = text.Substring(pos, 1);
			}

			size = e.Graphics.MeasureString(text, this.Font, new SizeF(this.Width, this.Height), sf);
			sizeStart = e.Graphics.MeasureString(textStart, this.Font, new SizeF(this.Width, this.Height), sf);
			sizeShCut = e.Graphics.MeasureString(textShCut, this.Font, new SizeF(this.Width, this.Height), sf);

			float xLeft, xCenter, xRight, yTop, yMiddle, yBottom;
			float textX, textY;

			int canvasWidth = this.Width - mSizingMargin.Horizontal - mContentMargin.Horizontal;
			int canvasHeight = this.Height - mSizingMargin.Vertical - mContentMargin.Vertical;

			xLeft = mSizingMargin.Left + mContentMargin.Left;
			xCenter = (canvasWidth - size.Width) / 2 + mSizingMargin.Left + mContentMargin.Left;
			xRight = canvasWidth - size.Width + mSizingMargin.Right + mContentMargin.Right;

			yTop = mSizingMargin.Top + mContentMargin.Top;
			yMiddle = (canvasHeight - size.Height) / 2 + mSizingMargin.Top + mContentMargin.Top;
			yBottom = canvasHeight - size.Height + mSizingMargin.Bottom + mContentMargin.Bottom;

			switch (this.TextAlign)
			{
				case ContentAlignment.TopLeft:
					textX = xLeft;
					textY = yTop;
					break;
				case ContentAlignment.TopCenter:
					textX = xCenter;
					textY = yTop;
					break;
				case ContentAlignment.TopRight:
					textX = xRight;
					textY = yTop;
					break;
				case ContentAlignment.MiddleLeft:
					textX = xLeft;
					textY = yMiddle;
					break;
				case ContentAlignment.MiddleCenter:
					textX = xCenter;
					textY = yMiddle;
					break;
				case ContentAlignment.MiddleRight:
					textX = xRight;
					textY = yMiddle;
					break;
				case ContentAlignment.BottomLeft:
					textX = xLeft;
					textY = yBottom;
					break;
				case ContentAlignment.BottomCenter:
					textX = xCenter;
					textY = yBottom;
					break;
				case ContentAlignment.BottomRight:
					textX = xRight;
					textY = yBottom;
					break;
				default:
					textX = 0;
					textY = 0;
					break;
			}
			drawRect = new RectangleF(textX, textY, textX + size.Width, textY + size.Height);

			e.Graphics.TextRenderingHint = Skin.TextRenderingHint;
			if (this.Enabled)
			{
				c = Skin.CachedProperties.ControlTextColor;
			}
			else
			{
				c = Skin.CachedProperties.ControlDisabledTextColor;
			}
		}

		private void DrawText(PaintEventArgs e)
		{
			if (this.Image == null || this.TextImageRelation == TextImageRelation.Overlay)
			{
				RectangleF drawRect = new RectangleF();
				string text = null;
				int pos = -1;
				SizeF sizeStart = new SizeF();
				SizeF sizeShCut = new SizeF();
				Color c = new Color();
				GenerateDrawText(e, out drawRect, out text, out pos, out sizeStart, out sizeShCut, out c);

				e.Graphics.DrawString(text, this.Font, new SolidBrush(c), drawRect);
				if (pos != -1)
				{
					e.Graphics.DrawLine(new Pen(c), drawRect.X + sizeStart.Width + 1, drawRect.Y + size.Height, drawRect.X + sizeStart.Width + sizeShCut.Width - 2, drawRect.Y + size.Height);
				}
			}

			// Set minimum size
			this.MinimumSize = new System.Drawing.Size(Convert.ToInt32(mContentMargin.Vertical + size.Width + 5), Convert.ToInt32(mContentMargin.Vertical + size.Height + 5));
		}

		#endregion

		#region Events
		void this_MouseEnter(object sender, EventArgs e)
		{
			mMState = MouseEventState.OnHover;
			this.Invalidate();
		}

		void this_MouseUp(object sender, MouseEventArgs e)
		{
			if (e.X < 0 || e.X > this.Width || e.Y < 0 || e.Y > this.Height)
			{
				mMState = MouseEventState.None;
			}
			else
			{
				mMState = MouseEventState.OnHover;
			}
			
			this.Invalidate();
		}

		void this_MouseDown(object sender, MouseEventArgs e)
		{
			mMState = MouseEventState.OnDown;
			this.Invalidate();
		}

		void this_MouseLeave(object sender, EventArgs e)
		{
			mMState = MouseEventState.None;
			this.Invalidate();
		}

		void this_MouseHover(object sender, EventArgs e)
		{
			mMState = MouseEventState.OnHover;
		}

		void DefaultLookAndFeel_OnSkinChanged(object sender, EventArgs e)
		{
			InitializeMargins();
		}
		
		#endregion

		#region ISkinnable Members

		/// <summary>
		/// Use default skin?
		/// </summary>
		[Category("Look && feel")]
		public bool UseDefaultSkin
		{
			get
			{
				return mUseDefaultSkin;
			}
			set
			{
				mUseDefaultSkin = value;
				Invalidate();
			}
		}

		/// <summary>
		/// Used skin
		/// </summary>
		[Category("Look && feel")]
		public Skin Skin
		{
			get
			{
				if (this.UseDefaultSkin)
				{
					return DefaultLookAndFeel.Skin;
				}

				return mSkin;
			}
			set
			{
				mSkin = value;
				Invalidate();
			}
		}

		#endregion
	}
}
